package com.example;

import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.TextAlignment;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHeight;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageMar;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageSz;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblLayoutType;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTrPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STPageOrientation;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblLayoutType;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.IntStream;

public class ExportUtils {

    DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    public void printCalendar(String filename, int year) throws IOException {
        try (XWPFDocument doc = new XWPFDocument();
             OutputStream out = new FileOutputStream(filename)) {
            // 设置纸张方向
            setPageOrient(doc);
            IntStream.rangeClosed(1, 12).forEach(month -> createTable(doc, year, month));
            doc.write(out);
        }
    }

    private List<List<CalendarWrapper>> createCalendar(int year, int month) {
        LocalDate startDate = LocalDate.of(year, month, 1);
        LocalDate endDate = startDate.with(TemporalAdjusters.lastDayOfMonth());
        LunarCalendarFestival festival = new LunarCalendarFestival();

        List<List<CalendarWrapper>> weekList = new ArrayList();
        while (startDate.isBefore(endDate)) {
            List<CalendarWrapper> dayList = new ArrayList();
            for (int i = 1; i <= 7; i++) {
                if (i < startDate.getDayOfWeek().getValue()) {
                    LocalDate currentDay = startDate.minusDays(startDate.getDayOfWeek().getValue() - i);
                    festival.initLunarCalendarInfo(currentDay.format(fmt));
                    dayList.add(CalendarWrapper.builder()
                            .day(currentDay.getDayOfMonth())
                            .festival(festival.showText())
                            .thisMonth(false)
                            .build());
                } else {
                    festival.initLunarCalendarInfo(startDate.format(fmt));
                    if (startDate.isBefore(endDate) || startDate.isEqual(endDate)) {
                        dayList.add(CalendarWrapper.builder()
                                .day(startDate.getDayOfMonth())
                                .festival(festival.showText())
                                .build());
                    } else {
                        dayList.add(CalendarWrapper.builder()
                                .day(startDate.getDayOfMonth())
                                .festival(festival.showText())
                                .thisMonth(false)
                                .build());
                    }
                    startDate = startDate.plusDays(1);
                }
            }
            weekList.add(dayList);
        }
        return weekList;
    }

    private void createTable(XWPFDocument doc, int year, int month) {
        List<List<CalendarWrapper>> weekList = createCalendar(year, month);

        XWPFTable table = doc.createTable(7, 7);
        CTTblLayoutType type = table.getCTTbl().getTblPr().addNewTblLayout();
        type.setType(STTblLayoutType.FIXED);
        // 设置表格宽度
        setTableWidth(table, 15000);
        // 设置列宽
        setColumnWidth(table, 2000);
        // 设置行高
        setRowHeight(table);
        // 合并表格标题
        mergeCells(table, 0, 0, 0, 6);

        fillCellCenter(table.getRow(0).getCell(0), year + "年" + month + "月", "宋体", 18);
        fillCellCenter(table.getRow(1).getCell(0), "星期一", "宋体", 10);
        fillCellCenter(table.getRow(1).getCell(1), "星期二", "宋体", 10);
        fillCellCenter(table.getRow(1).getCell(2), "星期三", "宋体", 10);
        fillCellCenter(table.getRow(1).getCell(3), "星期四", "宋体", 10);
        fillCellCenter(table.getRow(1).getCell(4), "星期五", "宋体", 10);
        fillCellCenter(table.getRow(1).getCell(5), "星期六", "宋体", 10);
        fillCellCenter(table.getRow(1).getCell(6), "星期日", "宋体", 10);

        IntStream.range(0, weekList.size())
                .forEach(weekIndex -> IntStream.range(0, weekList.get(weekIndex).size())
                        .forEach(dayIndex -> {
                            CalendarWrapper calendarWrapper = weekList.get(weekIndex).get(dayIndex);
                            if (Objects.nonNull(calendarWrapper.getDay())) {
                                String content = calendarWrapper.getDay() + "\r" + calendarWrapper.getFestival().trim();
                                if (Objects.nonNull(table.getRow(weekIndex + 2))) {
                                    XWPFTableCell cell = table.getRow(weekIndex + 2).getCell(dayIndex);
                                    if (!calendarWrapper.isThisMonth()) {
                                        cell.getCTTc().addNewTcPr().addNewShd().setFill("F5F5F5");
                                        fillCellLeft(cell, content, "宋体", 10, "808080");
                                    } else if (dayIndex == 5 || dayIndex == 6) {
                                        cell.getCTTc().addNewTcPr().addNewShd().setFill("FFD9CC");
                                        fillCellLeft(cell, content, "宋体", 10);
                                    } else {
                                        fillCellLeft(cell, content, "宋体", 10);
                                    }
                                }
                            }
                        }));
        createParagraph(doc);
    }

    /**
     * 设置表格宽度
     *
     * @param table
     * @param width
     */
    private void setTableWidth(XWPFTable table, int width) {
        CTTblPr tblPr = table.getCTTbl().getTblPr();
        tblPr.getTblW().setType(STTblWidth.DXA);
        tblPr.getTblW().setW(BigInteger.valueOf(width));
    }

    /**
     * 设置页面方向
     *
     * @param doc
     */
    private void setPageOrient(XWPFDocument doc) {
        CTBody body = doc.getDocument().addNewBody();
        CTSectPr sectPr = body.addNewSectPr();
        // 页面方向和纸张大小
        CTPageSz pageSz = sectPr.isSetPgSz() ? sectPr.getPgSz() : sectPr.addNewPgSz();
        pageSz.setOrient(STPageOrientation.LANDSCAPE);
        pageSz.setW(BigInteger.valueOf(842 * 20));
        pageSz.setH(BigInteger.valueOf(595 * 20));

        // 设置页边距
        CTPageMar pageMar = sectPr.addNewPgMar();
        pageMar.setLeft(BigInteger.valueOf(567));
        pageMar.setTop(BigInteger.valueOf(567));
        pageMar.setRight(BigInteger.valueOf(567));
        pageMar.setBottom(BigInteger.valueOf(567));
    }

    /**
     * 设置表格列宽
     *
     * @param table
     */
    private void setColumnWidth(XWPFTable table, int columnWidth) {
        table.getRows().forEach(row -> {
            CTTrPr trPr = row.getCtRow().addNewTrPr();
            CTHeight ht = trPr.addNewTrHeight();
            ht.setVal(BigInteger.valueOf(columnWidth));
        });
    }

    /**
     * 设置表格行高
     *
     * @param table
     */
    private void setRowHeight(XWPFTable table) {
        for (int i = 0; i < table.getRows().size(); i++) {
            CTTrPr trPr = table.getRows().get(i).getCtRow().addNewTrPr();
            CTHeight ht = trPr.addNewTrHeight();
            if (i == 0) {
                ht.setVal(BigInteger.valueOf(800));
            } else if (i == 1) {
                ht.setVal(BigInteger.valueOf(560));
            } else {
                ht.setVal(BigInteger.valueOf(1800));
            }
        }
    }

    private void mergeCells(XWPFTable table, int fromRow, int fromColumn, int toRow, int toColumn) {
        Integer width = table.getWidth() * (toColumn - fromColumn + 1) / table.getRow(fromRow).getTableCells().size();
        table.getRow(fromRow).getCell(fromColumn).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
        IntStream.rangeClosed(fromRow, toRow)
                .forEach(rowIndex ->
                        IntStream.rangeClosed(fromColumn + 1, toColumn)
                                .forEach(columnIndex ->
                                        table.getRow(rowIndex).getCell(columnIndex).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE)));
        table.getRow(fromRow).getCell(fromColumn).getCTTc().getTcPr().addNewTcW().setW(BigInteger.valueOf(width - 1));
    }

    private void fillCellCenter(XWPFTableCell cell, String content, String fontFamily, int fontSize) {
        cell.removeParagraph(0);
        cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
        XWPFParagraph paragraph = cell.addParagraph();
        XWPFRun xwpfRun = paragraph.createRun();
        xwpfRun.setText(content);
        xwpfRun.setFontFamily(fontFamily);
        xwpfRun.setFontSize(fontSize);
        xwpfRun.setBold(true);
        paragraph.setVerticalAlignment(TextAlignment.CENTER);
        paragraph.setAlignment(ParagraphAlignment.CENTER);
    }

    private void fillCellLeft(XWPFTableCell cell, String content, String fontFamily, int fontSize, String color) {
        cell.removeParagraph(0);
        cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.TOP);
        XWPFParagraph paragraph = cell.addParagraph();
        XWPFRun xwpfRun = paragraph.createRun();
        xwpfRun.setText(content);
        xwpfRun.setFontFamily(fontFamily);
        xwpfRun.setFontSize(fontSize);
        xwpfRun.setBold(true);
        if (Objects.nonNull(color)) {
            xwpfRun.setColor(color);
        }
        paragraph.setVerticalAlignment(TextAlignment.TOP);
        paragraph.setAlignment(ParagraphAlignment.LEFT);
    }

    private void fillCellLeft(XWPFTableCell cell, String content, String fontFamily, int fontSize) {
        fillCellLeft(cell, content, fontFamily, fontSize, null);
    }

    private void createParagraph(XWPFDocument doc) {
        XWPFParagraph paragraph = doc.createParagraph();
        XWPFRun run = paragraph.createRun();
        run.setText("");
    }
}
